﻿using System;
using System.Linq;
using System.Web.Mvc;
using Actya.Core;
using Actya.Core.Infrastructure;
using Actya.Core.SiteStructure;
using Actya.Web.Infrastructure.AntiForgery;
using Actya.Web.Mvc.Helpers;
using Actya.Web.ViewModels.Admin;
using Actya.Core.Util;

namespace Actya.Web.Controllers.CmsAdmin
{
	[Authorize(Roles = RoleNames.CmsAdministrators)]
	public class SitemapController : Controller
	{
		private readonly ICmsContext _cmsContext;
		private readonly ISiteService _siteService;
		private readonly IPageService _pageService;

		public SitemapController(ICmsContext cmsContext, ISiteService siteService, IPageService pageService)
		{
			_cmsContext = cmsContext;
			_siteService = siteService;
			_pageService = pageService;
		}

		public ActionResult Index()
		{
			var viewModel = new EditSitemapViewModel {SitemapNodes = _cmsContext.Site.SitemapNodes};
			return View(viewModel);
		}

		public ActionResult ItemAjax(string nodeId)
		{
			var node = _cmsContext.Site.FindSitemapNodeById(nodeId);
			return PartialView("_ViewSitemapItem", node);
		}

		public ActionResult EditAjax(string nodeId)
		{
			var node = _cmsContext.Site.FindSitemapNodeById(nodeId);
			SetPagePropertiesToViewBag(node);
			return PartialView("_EditSitemapItem", node);
		}

		public ActionResult Create()
		{
			var viewModel = new EditSitemapViewModel {NewNodeShowInNavigation = true, NewNodeShowInSitemap = true };
			return View(viewModel);
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult Create(EditSitemapViewModel model)
		{
			if (ModelState.IsValid)
			{
				var siteMapNode = new SitemapNode();
				siteMapNode.Title = model.NewNodeTitle;
				siteMapNode.Url = model.NewNodeUrl;
				siteMapNode.ShowInNavigation = model.NewNodeShowInNavigation;
				siteMapNode.ShowInSitemap = model.NewNodeShowInSitemap;
				siteMapNode.PageId = model.NewNodePageId;
				var site = _cmsContext.Site;
				// Set as primary reference when no other node references the page.
				siteMapNode.IsPrimaryPageReference = !String.IsNullOrEmpty(siteMapNode.PageId)
													 && site.FindSitemapNodeByPageId(siteMapNode.PageId) == null;
				var parentNode = site.FindSitemapNodeById(model.NewNodeParentId);
				siteMapNode.SetParent(site, parentNode);

				this._siteService.UpdateSite(site);
				return RedirectToAction("Index");
			}
			model.SitemapNodes = _cmsContext.Site.SitemapNodes;
			return View("Create", model);
		}

		[HttpPost]
		[AjaxValidateAntiForgeryToken]
		public ActionResult UpdateAjax(SitemapNode node)
		{
			if (ModelState.IsValid)
			{
				var nodeFromDb = _cmsContext.Site.FindSitemapNodeById(node.Id);
				nodeFromDb.Title = node.Title;
				nodeFromDb.Url = node.Url;
				nodeFromDb.PageId = node.PageId;
				nodeFromDb.ShowInNavigation = node.ShowInNavigation;
				nodeFromDb.ShowInSitemap = node.ShowInSitemap;
				try
				{
					_siteService.UpdateSite(_cmsContext.Site);
					this.ShowMessage(MessageType.Success, string.Format("Item '{0}' is updated successfully.", nodeFromDb.Title));
					return ItemAjax(nodeFromDb.Id);
				}
				catch (Exception ex)
				{
					this.ShowMessage(MessageType.Error, ex.Message);
				}
			}
			SetPagePropertiesToViewBag(node);
			return PartialView("_EditSitemapItem", node);
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult Delete(string id)
		{
			var site = _cmsContext.Site; 
			var node = site.FindSitemapNodeById(id);
			site.RemoveNodeFromSitemap(node);
			try
			{
				_siteService.UpdateSite(site);
				this.ShowMessage(MessageType.Success, string.Format("Item '{0}' is removed successfully.", node.Title), true);
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Index");
		}

		[HttpPost]
		[AjaxValidateAntiForgeryToken]
		public ActionResult SortNodes(string parentNodeId, string[] orderedChildNodeIds)
		{
			var site = _cmsContext.Site;
			try
			{
				site.SortNodes(parentNodeId, orderedChildNodeIds);
				_siteService.UpdateSite(site);
				this.ShowMessage(MessageType.Success, "Order has changed successfully.");
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return new EmptyResult();
		}

		[HttpPost]
		[AjaxValidateAntiForgeryToken]
		public ActionResult ChangeParent(string nodeId, string newParentNodeId)
		{
			var site = _cmsContext.Site;
			try
			{
                var node = site.FindSitemapNodeById(nodeId);
			    SitemapNode newParentNode = null;
                if (! String.IsNullOrEmpty(newParentNodeId))
                {
                    newParentNode = site.FindSitemapNodeById(newParentNodeId);
                }
				node.SetParent(site, newParentNode);
				_siteService.UpdateSite(site);
				this.ShowMessage(MessageType.Success, "Item is moved successfully.", true);
				return Json("Success");
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message);
			}
			return new EmptyResult();
		}

		public ActionResult GetFlattenedSitemapNodes()
		{
			var sitemapNodes = _cmsContext.Site.SitemapNodes.SelectDeep(n => n.SitemapNodes);
			var viewModel = from n in sitemapNodes
			                select new SitemapNodeViewModel
			                       	{
										Id = n.Id,
			                       		Title = n.Title,
			                       		Url = GetUrlForParent(n),
			                       		PageId = n.PageId,
			                       		Level = n.Level
			                       	};
			var result = Json(viewModel);
			result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
			return result;
		}

		private string GetUrlForParent(SitemapNode sitemapNode)
		{
			if (sitemapNode.Url == null || sitemapNode.Url.Contains("://"))
			{
				var url = sitemapNode.Title + "/";
				if (sitemapNode.Parent != null && ! String.IsNullOrEmpty(sitemapNode.Parent.Url))
				{
					url = sitemapNode.Parent.Url + url;
				}
				else
				{
					url = "/" + url;
				}
				return url;
			}
			return sitemapNode.Url;
		}

		private void SetPagePropertiesToViewBag(SitemapNode node)
		{
			if (! String.IsNullOrEmpty(node.PageId))
			{
				var page = _pageService.GetPageById(node.PageId);
				if (page != null)
				{
					ViewBag.PageTitle = page.Title;
					ViewBag.PageUrl = page.Url;
				}
				else
				{
					// page not found, set pageId to null, so we can edit the url.
					node.PageId = null;
				}
			}
		}
	}
}
